package com.tifa.myssm.baseDao;

import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class BaseDao<T> {
    protected Connection conn;
    protected PreparedStatement psmt;
    protected ResultSet rs;
    private Class entityClass;

    public BaseDao() {
        Type genericSuperclass = getClass().getGenericSuperclass();
        ParameterizedType pargenericSuperclass = (ParameterizedType) genericSuperclass;
        Type[] actualTypeArguments = pargenericSuperclass.getActualTypeArguments();
        Type actualTypeArgument = actualTypeArguments[0];
        String typeName = actualTypeArgument.getTypeName();
        try {
            entityClass = Class.forName(typeName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    protected Connection getConn() {
        return ConnUtil.getConn();
    }

    protected void close(Connection conn, PreparedStatement psmt, ResultSet rs) {
        try {
            if (Objects.nonNull(conn)) {
                conn.close();
            }
            if (Objects.nonNull(psmt)) {
                psmt.close();
            }
            if (Objects.nonNull(rs)) {
                rs.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    protected T load(String sql, Object... params) {
        conn = ConnUtil.getConn();
        try {
            psmt = conn.prepareStatement(sql);
            setParams(psmt, params);
            rs = psmt.executeQuery();
            ResultSetMetaData metaData = rs.getMetaData();
            var columnCount = metaData.getColumnCount();
            if (rs.next()) {
                T entity = (T) entityClass.getDeclaredConstructor().newInstance();
                for (int i = 0; i < columnCount; i++) {
                    var obj = (T) rs.getObject(i + 1);
                    var columnName = metaData.getColumnName(i + 1);
                    setValue(entity, columnName, obj);
                }
                return entity;
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseDaoException("查询一个实体出错");
        }
        return null;
    }

    protected int executeUpdate(String sql, Object... params) {
        boolean insertFlag = sql.trim().toUpperCase().startsWith("INSERT");
        conn = getConn();
        try {
            if (insertFlag) {
                psmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            } else {
                psmt = conn.prepareStatement(sql);
            }
            setParams(psmt, params);
            int influencedCount = psmt.executeUpdate();
            if (insertFlag) {
                rs = psmt.getGeneratedKeys();
                if (rs.next()) {
                    return ((Long) rs.getLong(1)).intValue();
                }
            }
            return influencedCount;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseDaoException("BaseDao出错了");
        }
    }

    private void setParams(PreparedStatement psmt, Object[] params) throws SQLException {
        if (Objects.isNull(psmt)) {
            return;
        }
        for (int i = 0; i < params.length; i++) {
            psmt.setObject(i + 1, params[i]);
        }
    }

    private boolean isMyType(String typeName) {
        if (StringUtils.equals(typeName, Integer.class.getName()) ||
                StringUtils.equals(typeName, LocalDateTime.class.getName()) ||
                StringUtils.equals(typeName, java.util.Date.class.getName()) ||
                StringUtils.equals(typeName, Date.class.getName()) ||
                StringUtils.equals(typeName, String.class.getName())) {
            return false;
        }
        return true;
    }

    private boolean isNotMyType(String typeName) {
        return !isMyType(typeName);
    }

    protected List<T> executeQuery(String sql, Object... params) {
        List<T> list = new ArrayList<>();
        conn = getConn();
        try {
            psmt = conn.prepareStatement(sql);
            setParams(psmt, params);
            rs = psmt.executeQuery();
            ResultSetMetaData metaData = rs.getMetaData();
            int columnCount = metaData.getColumnCount();
            while (rs.next()) {
                T obj = (T) entityClass.getDeclaredConstructor().newInstance();
                for (int i = 0; i < columnCount; i++) {
                    var columnName = metaData.getColumnLabel(i + 1);
                    var columnValue = rs.getObject(i + 1);
                    setValue(obj, columnName, columnValue);
                }
                list.add(obj);
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseDaoException("BaseDao查询列表出错");
        }
        return list;
    }

    private void setValue(T obj, String propertyName, Object propertyValue) throws Exception {
        Class clazz = obj.getClass();
        Field declaredField = clazz.getDeclaredField(propertyName);
        if (Objects.isNull(declaredField)) {
            return;
        }
        String typeName = declaredField.getType().getName();
        if (isMyType(typeName)) {
            Class myClazz = Class.forName(typeName);
            propertyValue = myClazz.getDeclaredConstructor(Integer.class).newInstance(propertyValue);
        }

        declaredField.setAccessible(true);
        declaredField.set(obj, propertyValue);
    }
}
